package com.ryan.ohos.extension.event;

import com.ryan.ohos.extension.event.interfaces.ComponentParentHack;
import com.ryan.ohos.extension.event.interfaces.View;
import com.ryan.ohos.extension.event.interfaces.ViewGroup;
import com.ryan.ohos.extension.compact.TouchEventCompact;
import ohos.agp.components.Component;
import ohos.agp.components.ComponentContainer;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.multimodalinput.event.MmiPoint;
import ohos.multimodalinput.event.TouchEvent;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * 事件分发帮助类，辅助{@link View}与{@link ViewGroup}分发事件。
 *
 * 在{@link Component.TouchEventListener#onTouchEvent(Component, TouchEvent)}
 * 调用{@link #dispatch(Component, TouchEvent)}来分发事件。
 */
public class DispatchHelper {

    /** 暂存所有的实现了View接口的控件 **/
    private static final List<Component> nodes = new ArrayList<>();
    /** 暂存每次事件的处理结果 **/
    private static final HashMap<Integer, Boolean> records = new HashMap<>();

    /** 暂存上次事件，用于过滤由于自下而上的事件冒泡与自上而下的事件分发机制而产生的多次分发 **/
    private static String lastEvent = "";
    private final static TouchEventCompact compact = new TouchEventCompact(true);
    private final static TouchEventProxy proxy = new TouchEventProxy();

    /**
     * 在{@link Component.TouchEventListener#onTouchEvent(Component, TouchEvent)}中调用此函数来分发事件。
     * @param component 需分发事件的控件
     * @param touchEvent 需分发的事件
     * @return 事件处理的结果
     */
    public static boolean dispatch(Component component, TouchEvent touchEvent) {
        proxy.setEvent(touchEvent);

        // 过滤由于自下而上的事件冒泡 与 自上而下的事件分发机制而产生的重复分发
        if (isSameEvent(proxy)) {
            return true;
        }

        // 纠正通过getPointerPosition获取的y坐标的偏移
//        compact.correct(touchEvent);

        lastEvent = convertEvent(proxy);

        int action = touchEvent.getAction();
        if (action == TouchEvent.PRIMARY_POINT_DOWN) {
            clearNodes();
        }

        if (nodes.size() <= 0) createNodes(component);
        dispatch(nodes.size(), 1, proxy);
//        collectRecords();

//        boolean result = findRecord(component);

        if (action == TouchEvent.PRIMARY_POINT_UP) {
            clearNodes();
        }

        return true;
    }

    /**
     * 当子控件不想父控件拦截事件时，调用此方法
     *
     * @param component 不想事件被拦截的控件
     * @param disallowIntercept true为不拦截
     */
    public static void requestDisallowInterceptTouchEvent(Component component, boolean disallowIntercept) {
        if (component.getComponentParent() instanceof ViewGroup) {
            ((ViewGroup) component.getComponentParent()).requestDisallowInterceptTouchEvent(disallowIntercept);
        }
    }

    /**
     * 当子控件不想父控件拦截事件时，在{@link EventHandler#postTask(Runnable)}中调用此方法
     *
     * @param component 不想事件被拦截的控件
     * @param disallowIntercept true为不拦截
     */
    public static void postRequestDisallowInterceptTouchEvent(Component component, boolean disallowIntercept) {
        EventHandler handler = new EventHandler(EventRunner.getMainEventRunner());
        handler.postTask(() -> requestDisallowInterceptTouchEvent(component, disallowIntercept));
    }

    public static TouchEventCompact getTouchEventCompact() {
        return compact;
    }

    /**
     * 自顶到下的事件分发，如果最上层的父控件没有实现{@link ViewGroup},则找寻下一个实现了{@link ViewGroup}的控件来分发事件。
     *
     * @param size {@link #nodes}的size
     * @param i 寻找实现了{@link ViewGroup}的控件的次数，初始为1，自增
     * @param touchEvent 传递的事件
     * @return 事件分发的结果
     */
    private static boolean dispatch(int size, int i, TouchEvent touchEvent) {
        boolean result = false;
        if (size > 0) {
            Component node = nodes.get(size - i);
            if (node instanceof ViewGroup) {
                ViewGroup group = (ViewGroup) node;
                result = group.dispatchTouchEvent(touchEvent);
            } else if (node instanceof View) {
                View view = (View) node;
                result = view.dispatchTouchEvent(touchEvent);
            } else {
                if (i < size) {
                    i++;
                    result = dispatch(size, i, touchEvent);
                }
            }
        }

        return result;
    }

    private static void collectRecords() {
        records.clear();
        for (int i = 0; i < nodes.size(); i++) {
            records.put(i, ((View) nodes.get(i)).isConsumed());
        }
    }

    private static boolean findRecord(Component component) {
        int i = nodes.indexOf(component);
        if (i < 0) return false;
        return records.get(i);
    }

    private static void clearNodes() {
        nodes.clear();
    }

    private static void createNodes(Component component) {
        if (component instanceof View) nodes.add(component);
        if (component.getComponentParent() != null) {
            createNodes((Component) component.getComponentParent());
        } else {
            if (component instanceof ComponentParentHack) {
                ComponentContainer c = ((ComponentParentHack) component).getComponentParentHack();
                if (c != null) {
                    createNodes(c);
                }
            }
        }
    }

    private static String convertEvent(TouchEvent event) {
        String split = ",";
        MmiPoint point = event.getPointerScreenPosition(event.getIndex());
        return event.getAction() + split + point.getX() + split +
                point.getY() + split + event.hashCode();
    }

    private static boolean isSameEvent(TouchEvent event) {
        return lastEvent.equals(convertEvent(event));
    }
}
